package datastruct.dict.impl;

import datastruct.dict.inter.IDict;
import datastruct.dict.inter.IDictConsumer;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class JoConcurrentMap implements IDict {

    private ConcurrentHashMap<String, Object> hm;

    public JoConcurrentMap() {
        hm = new ConcurrentHashMap<>();
    }

    public static JoConcurrentMap makeConcurrentMap() {
        return new JoConcurrentMap();
    }

    @Override
    public Object get(String key) {
        return hm == null || hm.size()==0 ? null : hm.get(key);
    }

    public boolean contains(String key) {
        return hm != null && hm.containsKey(key);
    }

    @Override
    public int len() {
        return hm == null ? 0 : hm.size();
    }

    @Override
    public int put(String key, Object val) {
        if (hm == null) {
            return 0;
        }
        if (hm.containsKey(key)) {
            hm.put(key, val);
            return 0;
        }
        hm.put(key, val);
        return 1;
    }

    @Override
    public int putIfAbsent(String key, Object val) {
        if (hm == null || hm.containsKey(key)) {
            return 0;
        }
        hm.put(key, val);
        return 1;
    }

    @Override
    public int putIfExists(String key, Object val) {
        if (hm == null || !hm.containsKey(key)) {
            return 0;
        }
        hm.put(key, val);
        return 1;
    }

    @Override
    public int remove(String key) {
        if (hm == null || !hm.containsKey(key)) {
            return 0;
        }
        hm.remove(key);
        return 1;
    }

    @Override
    public void forEach(IDictConsumer iConsumer) {
        for (Map.Entry<String, Object> entry : hm.entrySet()) {
            boolean isContinue = iConsumer.consumer(entry.getKey(), entry.getValue());
            if (!isContinue) {
                return;
            }
        }
    }

    @Override
    public String[] keys() {
        String[] ret = new String[hm.size()];
        int i = 0;
        for (String key : hm.keySet()) {
            ret[i] = key;
            i++;
        }
        return ret;
    }

    public String randomKey() {
        if (hm == null || hm.size() == 0) {
            return null;
        }
        Random r = new Random();
        int index = r.nextInt(hm.size());
        int i = 0;
        for (String key : hm.keySet()) {
            if (i == index) {
                return key;
            }
            i++;
        }
        return null;
    }

    @Override
    public String[] randomKeys(int limit) {
        if (hm == null || hm.size() == 0) {
            return null;
        }
        if (limit >= hm.size()) {
            return keys();
        }

        String[] ret = new String[limit];
        for (int i=0;i<limit;) {
            String s = randomKey();
            if (s != null) {
                ret[i] = s;
                i++;
            }
        }
        return ret;
    }

    @Override
    public String[] randomDistinctKeys(int limit) {
        limit = Math.min(limit, hm.size());
        HashMap<String, Boolean> hashMap = new HashMap<>();
        while (hashMap.size() < limit) {
            String k = randomKey();
            if (k != null) {
                hashMap.put(k, true);
            }
        }

        String[] ret = new String[limit];
        int i = 0;
        for (String k : hashMap.keySet()) {
            ret[i] = k;
            i++;
        }
        return ret;
    }

    @Override
    public void clear() {
        hm.clear();
    }

    @Override
    public Object[] diff(IDict[] dicts) {
        IDict anotherSet = new JoSimpleMap();
        for(IDict dt : dicts) {
            if (dt == null) {
                continue;
            }
            dt.forEach(new IDictConsumer() {
                @Override
                public boolean consumer(String key, Object val) {
                    anotherSet.put(key, true);
                    return true;
                }
            });
        }

        String[] ks = keys();
        ArrayList<String> res = new ArrayList<>();

        for (String k : ks) {
            if (!anotherSet.contains(k)) {
                res.add(k);
            }
        }

        return res.toArray(new String[0]);
    }

    @Override
    public Object[] union(IDict[] dicts) {
        HashMap<String, Boolean> set = new HashMap<>();
        String[] ks = keys();
        for (String s : ks) {
            set.put(s, true);
        }

        for (IDict dt : dicts) {
            dt.forEach(new IDictConsumer() {
                @Override
                public boolean consumer(String key, Object val) {
                    set.put(key, true);
                    return true;
                }
            });
        }

        return set.keySet().toArray(new String[0]);
    }
}
